home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
PROGRAMM
/
CC_C
/
0151.ZIP
/
EXP.C
< prev
next >
Wrap
Text File
|
1985-02-09
|
23KB
|
914 lines
#include "bdscio.h"
/*******************************************************
:: * CP/M exp. ver. by F jennings May 1983 *
* *
:: * - TELINK - *
:: * - MODEM PROGRAM - *
* *
:: * T. Jennings 29 JAN 1983 *
* *
*******************************************************/
/* Another, but more complete modem program. This one provides
the file transfer modes as in Ward Christensen's programs,
and all the fancy character oriented stuff from TELNET, and
some new ones. */
/* Thanks to John Wiardo for 'CMODEM', from whence I got the
basic file transmission stuff, and Leor Zolman et al for the
good text oriented ideas. (And of course the 'C'...) */
/* Modem port characteristics. These are set for a FDC-1 teletek */
/* You will have to define the following for your system.........*/
#define MSTAT 03 /* Modem status port */
#define MDATA 02 /* Modem data port */
#define RDA 0x01 /* Modem input data ready mask */
#define TBE 0x04 /* Modem ready to send a character mask */
/* Cursor control sequences */
#define CLREOL "\033T" /* string to clear from cursor
to end of line. Use 10 or 20 spaces
followed by the same amount of
backspaces if not available. */
#define LEADIN "\033=" /* Lead in sequence for cursor
position.This string is typed
before outputing the line byte.
(See function PLACE(x,y) */
#define CLEAR_SCREEN "\033*" /* What else? This is actually
for a TELE-VIDEO 912 terminal. */
/* Also see place() routine and fix up for your terminal.*/
/* * * * * * * * * * End of custom area. * * * * * * * * * */
#define NONE 0 /* Usual define area. */
#define SOH 1
#define CNTRLB 2
#define CNTRLC 3
#define EOT 4
#define CNTRLE 5
#define ACK 6
#define TAB 9
#define BS 8
#define LF 0x0a
#define CR 0x0d
#define CNTRLQ 17
#define CNTRLS 19
#define NAK 21
#define CNTRLZ 26
#define DEL 127
#define TIMEOUT -1
#define ERROR -1
#define SPECIAL 0x1b /* ESCape character, */
#define STOPCHAR 0x1b
#define CONIN 3
#define CONOUT 4
#define BOTTOM 23 /* line where text appears */
#define TOP 0 /* line where commands appear */
#define BSIZE 24096 /* local buffer size, BIG buffer */
#define ROOM (BSIZE-128) /* room we use, provide extra space */
#define CPMSIZ 128 /* CP/M record size, */
#define RETRYMAX 10 /* # times to retry on tx/rx error */
#define SPS 275 /* loop iteration fudge factor for
time delay */
/* File Buffers */
char filbuf[BUFSIZ]; /* xmit/rcv file buffer */
char txtbuf[BUFSIZ]; /* input text file buffer */
char sndbuf[BUFSIZ]; /* output text file buffer */
char colbuf[BSIZE]; /* buffer for text collection (24096) */
char buffer[CPMSIZ]; /* file I/O buffer, */
char linebuf[80]; /* console line input */
char savname[80]; /* text collecttion file name */
char cursor; /* cursor position (bottom line) */
char c; /* general purpose character, */
int ci; /* character from file I/O, for error checking,*/
int i; /* general purpose */
char autolf; /* if true, send LFs after CR's */
int count; /* characters in the buffer, */
char halfdup; /* half/full duplex if true/false */
char savflg; /* true is collecting text to a file, */
char chksum; /* file transmission checksum */
char sector; /* sector being sent/recieved */
char newsec; /* sector # from the other modem */
int errors; /* # block transmission errors */
char attempts; /* another error counter */
char parity; /* 0= no parity, 1= odd, 2 =even */
/* this is the main driver, where characters are sent back and forth
between the console and modem. Typing a special character allows
processing commands, such as opening files, etc. */
main()
{
cursor =0; /* initial cursor position, */
count =0; /* no text in buf yet, */
parity =0;
savflg= FALSE;
halfdup= FALSE;
autolf= FALSE;
clear(); /* clear the screen, */
place(TOP,0);
printf("TELINK Modem Utility... by T.Jennings Jan 1983");
PRINTF(" Hit ESCape to enter commands.");
signon(); /* sign on msg, etc */
place(BOTTOM,0);
linebuf[0]= 0x00; /* empty command line, */
while(1) {
if(c= keyhit()) { /* if console char ready,*/
if(c== SPECIAL) /* if its the special command char,*/
process(); /* go execute commands */
else { /* otherwise, */
if(halfdup) /* if half duplex mode, */
conout(c); /* echo it, */
modout(c); /* always send it to the modem, */
if((savflg) && (halfdup) &&(c != CNTRLZ)) {
colbuf[count++] =c;
}
if(( c==CR) && autolf) {
modout(LF);
if(halfdup && autolf) {
conout(LF);
}
if(savflg) {
colbuf[count++] =LF;
}
}
}
}
if(modstat()) { /* if modem ready, */
c= modin(10); /* get the character, */
conout(c); /* type it, */
if(savflg &&(c != CNTRLZ)) {
colbuf[count++] =c; /* save the character */
if(count > ROOM ) { /* if nearly full, */
modout(CNTRLS); /* tell sender to pause, */
/* but flush up to 32 chars while he */
/* gets around to stopping */
for(i=0; i<32; i++) {
ci=modin(50);
if(ci !=TIMEOUT) {
c= ci;
colbuf[count] =ci;
conout(colbuf[count++]);
}
else break;
} /* write them out to the file, */
for(i=0; i<count; i++) {
errors = putc(colbuf[i],txtbuf);
if(errors ==ERROR)
break;
}
if(errors ==ERROR) {
place(TOP,0);
printf(" *** Disk write error: ");
stopcollect();
printf(" ***");
}
count =0;
modout(CNTRLQ); /* OK to send again, */
}
}
}
}
}
/* Process special commands. As soon as we enter, (from MAIN),
put the cursor at the top, and enter commands there. Maintain
the status line. */
process()
{
char c;
place (TOP,0); /* go to the top of the screen, */
printf ("TELINK Command: "); /* clear some space, */
clreol();
c= lconin();
place (TOP,0);
switch(tolower(c)) {
case 'h':
printf("TELINK Command: Half duplex");
clreol();
halfdup =TRUE;
break;
case 'f':
printf ("TELINK Command: Full duplex");
clreol();
halfdup =FALSE;
break;
case 't':
transmit(); /* transmit a file, */
break;
case 'r':
recieve(); /* get a file, */
break;
case 'c':
collect(); /* collect text, */
break;
case 's':
stopcollect();
break;
case 'd':
down(); /* send a text file, */
break;
case SPECIAL:
printf("TELINK Command: ESCape sent.");
clreol();
modout(SPECIAL); /* send a special */
break;
case 'q':
if (savflg)
stopcollect();
place(BOTTOM,0);
clreol();
place (BOTTOM-1,0);
exit(); /* closes open files. */
break;
case 'a':
printf ("TELINK Command: Auto linefeed ON");
clreol();
autolf =TRUE; /* auto linefeed mode, */
break;
case 'm':
printf ("TELINK Command: Auto linefeed OFF");
clreol();
autolf =FALSE;
break;
case 'p':
set_parity();
break;
case '?':
printf("Status Pending:");
clreol();
summary();
break;
default:
printf("TELINK Command: Nothing changed.");
clreol();
signon(); /* list the commands */
break;
}
place (BOTTOM,cursor); /* restore the cursor, */
return;
}
/* Select a parity option. Parity is calculated in the modem
output routine. */
set_parity()
{
char c;
do {
place (TOP,0); printf ("(E)ven, (O)dd, or (N)o parity:");
clreol();
errors = FALSE;
c = lconin();
place(TOP,0);
switch(tolower(c)) {
case 'e':
parity =1; /* LSB set, */
break;
case 'o':
parity =2; /* LSB clear, but byte
break; non-zero, */
case 'n':
parity =0; /* byte zero, */
break;
default:
errors = TRUE; /* make the guy pick one. */
}
} while (errors);
return;
}
/* get a file through the modem */
recieve() /* new routine from MS-DOS Ver */
{
int i; /* was char i; here ?? */
char ackchar;
char secchk;
int firstchar;
int blknum;
printf("File to recieve: ");
clreol();
if (getstring(linebuf) == NULL) /* quit if blank line */
return;
errors = creat(linebuf,filbuf);
if(errors ==ERROR) {
place(TOP,0);
printf("Can't create %s",linebuf);
clreol();
return;
}
ackchar =NAK; /* for first time though */
sector =0;
blknum =0;
errors =0;
/* Loop here for each block. If the first character is SOH its a new
block. If it's EOF, there is no more blocks (close files).anything
else, or a TIMEOUT, is an error, falls through to bottom,
count errors etc. */
do {
place(TOP,0); printf("Waiting for block %d: ",blknum);
clreol();
modout(ackchar); /* ACK/NAK from previous sector */
ackchar=NAK; /* assume bad, will be changed if good */
do {
firstchar = modin(100);
}
while(firstchar !=SOH && firstchar !=EOT && firstchar !=TIMEOUT);
if(firstchar == TIMEOUT) {
++errors;
printf("still waiting...");
clreol();
}
/* SOH recieved. Assume a new block, and attempt to get the rest of
the block. We'll time out if nothing recieved. */
if(firstchar == SOH) { /* start of header, */
newsec = modin(10);
if(newsec +modin(10)==255) { /* if correct sector, */
secchk =sector+1;
if(newsec == secchk) {
chksum = 0;
for(i =0; i < CPMSIZ; i++) {
buffer[i] = modin(10); /* read bytes */
chksum +=buffer[i];
}
if(chksum == modin(10)) {
errors = 0;
ackchar =ACK;
sector =newsec;
++blknum;
i = write(filbuf,buffer,1);
if(i ==ERROR) {
errors =RETRYMAX;
printf("Disk write error: ");
break;
}
printf("received OK");
}
else {
printf("checksum error");
++errors;
}
}
else if(newsec == sector) { /* same sector as last */
while(modin(10) != TIMEOUT); /* flush bytes */
printf("duplicate sector %d, ignored.",blknum);
ackchar =ACK;
}
else {
printf("block sync error");
++errors;
}
}
else {
printf("header error");
++errors;
}
}
if(errors) {
while(modin(10) != TIMEOUT); /* flush any extra, */
}
if (keyhit() ==STOPCHAR) {
errors =RETRYMAX;
}
if (errors >= RETRYMAX) {
place(TOP,0);
if(ask("Abort or errors: try again? ")) {
errors =0; /* lets try again */
ackchar =NAK;
}
}
}
while(firstchar != EOT && errors < RETRYMAX);
modout(ACK);
fclose(filbuf);
place(TOP,0);
if((firstchar == EOT) && (errors < RETRYMAX)) {
printf("Transfer complete, %d blocks received.",blknum);
clreol();
}
else {
printf("Transfer aborted at block %d.",blknum);
clreol();
}
return;
}
/* Transmit a file down the wire. This transmits in full Ward Cristensen
block format. */
transmit() /* new */
{
char eof;
char c;
int i;
int ci;
char sector;
char chksum;
int blknum;
printf("File to send: ");
clreol();
if (getstring(linebuf) == NULL)
return;
errors = fopen(linebuf,filbuf);
if(errors ==ERROR) {
place(TOP,0);
printf("Can't find %s!",linebuf);
clreol();
return;
}
place(TOP,0); printf("Waiting for the receiver, ");
clreol();
for (i=RETRYMAX; i > 0; i--) {
if ((ci =modin(40)) ==NAK) /* wait for initial NAK */
break;
}
if (i == 0) {
printf(" not ready."); /* never got it. */
return;
}
errors =0; /* reset the error counter, */
eof =FALSE;
sector =1; /* sector starts at 1, */
blknum =0;
while(!eof && (errors < RETRYMAX)) { /* while more chars, */
i =read(filbuf,buffer,1); /* fill buffer, */
eof = (i == NONE); /* one block only */
do { /* until we get an ACK, */
place(TOP,0); printf("Sending block %d, ",blknum);
clreol();
modout(SOH); /* tell rcvr a block comes */
modout(sector); /* send block #, */
modout(~sector); /* block check, */
chksum =0;
for(i =0;i < CPMSIZ; i++) {
c=buffer[i];
modout(c); /* send 128 data bytes, */
chksum += c; /*(keep check sum) */
}
modout(chksum); /* send check sum, */
i =RETRYMAX;
do { ci =modin(50); /* get the acknowledge char*/
} while ((ci == TIMEOUT) && --i);
++errors; /* assume bad for now */
if (keyhit() ==STOPCHAR) {
errors =RETRYMAX; /* abort now */
ci =TIMEOUT; /* so it falls thru */
}
if (errors >= RETRYMAX) {
if (ask("Abort or errors: try again?") ) {
errors =0;
}
}
} while((ci != ACK) && (errors < RETRYMAX));
/* Either block sent OK or too many errors. */
if (ci ==ACK) {
errors =0; /* sent OK, */
printf(" transmitted OK ");
sector++; /* next sector... */
++blknum;
}
}
modout(EOT); /* end of transmission */
modout(EOT);
attempts = 0;
while(modstat()) {
modin(10);
attempts++;
}
place (TOP,0);
if((errors < RETRYMAX) && (attempts < RETRYMAX))
printf("Transfer complete, sent %d blocks.",blknum);
else printf("Transfer aborted at block %d.",blknum);
clreol();
fclose (filbuf);
return;
}
/* Start the collection of ASCII data over the wire. create a file,
set the flag so that the MAIN will save characters. */
collect() /* new */
{
if(savflg) {
printf("Already collecting in file '%s'",savname);
clreol();
}
else {
printf("File to put text in: ");
clreol();
if (getstring(linebuf) == NULL)
return;
strcpy(savname,linebuf); /*save the filename */
/*for status display */
errors = fcreat(linebuf,txtbuf);
if (errors ==ERROR) {
place(TOP,0); printf("Can't create %s",linebuf);
clreol();
return;
}
else
savflg =TRUE; /* enable collection */
count =0; /* empty so far, */
}
return;
}
/* Stop collection, if any is under way. */
stopcollect() /* old */
{
int i;
if(savflg) { /*if chsracters left in the */
if(count) { /* local buffer, write them */
for(i=0; i<count; i++) { /* out */
putc(colbuf[i],txtbuf);
}
}
putc(CNTRLZ,txtbuf); /* was space */
fflush(txtbuf);
fclose(txtbuf);
savflg =FALSE;
printf("Collection stopped");
clreol();
}
else
printf("Not collecting!");
clreol();
return;
}
/* Send a text file to the modem, no format. After sending a CR or LF,
we wait for any prompts or CR LF sequences sent by the other
end to go away, allowing downloading messages to bulletin boards,*/
down() /* new */
{
char lastc,b,c;
int ci;
char stopflg;
printf("Text file to send: ");
clreol();
if (getstring(linebuf) == NULL)
return;
errors = fopen(linebuf,sndbuf);
if(errors == ERROR) {
place(TOP,0); printf("Can't find '%s'",linebuf);
clreol();
return;
}
place(BOTTOM,cursor); /* put the cursor back */
lastc = CR; /* in case 1st char is a CR, */
stopflg = FALSE;
while(((ci=getc(sndbuf))!=ERROR)&&((ci &0x7f) !=CNTRLZ) && !stopflg) {
c =ci &0x7f;
if(keyhit() == SPECIAL)
stopflg =TRUE; /* abort if ESCape */
if((c==CR) &&(lastc==CR)) /* dont allow blank lines, */
modout(' '); /* make at least 1 char */
if((c !=LF) || autolf) { /* totally ignore LF's,unless auto lf, */
modout(c); /* send the character, */
if(halfdup) { /* echo it to the console, */
conout(c); /* explicitly if halfdup, */
}
else {
b =modin(1);
if(b ==CNTRLS) {
b =wait();
}
conout(b);
}
lastc =c;
if(lastc ==CR) { /* if we just did a newline, */
do {
ci =modin(2); /* flush all the prompt or */
if(ci == CNTRLS)
ci =wait(); /* watch for cntrl-S, */
conout(ci); /* whatever */
}
while(ci != TIMEOUT);
}
if(modstat()) { /* always look for modem characters, */
b =modin(1);
if(b ==CNTRLS) /* if we get a control S, */
b =wait(); /* pause here, */
conout(b); /* then type it */
}
}
}
place(TOP,0);
printf("%s",(stopflg ? "Aborted" : "Transfer complete"));
clreol();
return;
}
/* Initialize the console, list the commands, etc. */
signon() /* new */
{
place (TOP+1,0); clreol(); printf("\n"); clreol();
printf(" H ......... Half Duplex F .......... Full duplex\n");
clreol();
printf(" A ......... Auto linefeed M ....... Disable Auto linefeed\n");
clreol();
printf(" C ......... Collect text S ....... Stop collecting text\n");
clreol();
printf(" T ......... Transmit a file R .......... Receive a file\n");
clreol();
printf(" D ......... Dump text ESC ........ Send an Escape\n");
clreol();
printf(" P ......... Select Parity Q ............. Quit\n");
clreol();
printf(" ? ......... List Status Any char....Reprints commands\n");
clreol(); return;
}
/* List the current setting of things. */
summary() { /* new */
place(TOP+1,0); clreol(); printf("\n"); clreol();
printf(" TELINK -- Modem Utility (C) T. Jennings 1983\n");
clreol();printf("\n");clreol();
printf("\tAuto linefeed %s\n",(autolf ? "ON" : "OFF"));
clreol();
if (savflg) {
printf("\tCollecting Text in File '%s'\n",savname);
} else
printf("\tNot Collecting Text... \n");
clreol();
printf("\tThe Cursor is at Column %d\n",cursor+1);
clreol();printf("\n");clreol();return;
}
/* Console character input, output, and status functions. */
char lconin() /* new original ver */
{
do(c = keyhit());
while(c ==0);
return (c);
}
char lconout(c) /* new */
char c;
{ bdos(6,c);
return;
}
/* Type a character on the console, maintaining correct cursor
position. All of the seperate returns are to speed things up,
so it doen't have to go by all of the IF's to get a return
at the end. */
conout(c)
char c;
{ c&= 0x7f; /*make into ASCII range */
if (c == ESC) /* don't send ESCapes,they */
return; /* make a mess of the screen */
if (c == CNTRLZ) /* same with ^Z, they erase the */
return; /* screen on ADM-3',TVI's etc. */
if (c == TAB) { /* expand tabs to spaces, */
do conout(' '); /* every eight columns. */
while (cursor % 8); /* (cursor)=mudolo 8 */
return; /* this is recursion... */
}
lconout(c); /* type a character */
if ((c > 0x1f) && (c < 0x7f)) {
++cursor; /* update cursor if printable */
return;
}
if ((c == CR) || (c == LF)) {
cursor =0; /* column 0 if CR or LF */
return;
}
if ((c == BS) && cursor != 0)
--cursor; /* do backspace, to */
return;
}
/* Modem routines: modin, modout, modstat. MODIN is strange,
since it supports a maximum timeout argument. */
char modstat()
{
char c;
c = inp(MSTAT); /* sample status, */
return(c&RDA);
}
/* Output a character to the modem. If the parity flag is set,
calculate parity. Parity is accumulated in the LSB of P; the other
bits contain garbage. The values of 'parity' are chosen to accomodate
this method: Add all the one bits together, adding in the parity
flag. The sum of the data bits is 1 if there are an odd number of
bits. Adding the parity flag (assume set to 'odd') leaves 0 as the
parity flag. Conversly, if even parity is selected ('parity' =1)
adding 1 to the sum of the data bits (1) makes a 0 parity bit. */
char modout(c)
char c;
{
char s;
int p,b,i;
if (parity) {
p = parity; b=c; /* initially 1 if even desired, */
for(i=0; i<7; i++) { /* do 7 bits, */
p+=b; /* accumulate bits in p, */
b = (b >> 1); /* next bit, */
} /* now lsb of P=1 means even */
c &= 0x7f; /* strip off MSB, */
c |= ((p <<7) &0x80); /* set/reset MSB with parity flag */
}
do {
s = inp(MSTAT) &TBE;
}
while (s==0);
outp (MDATA,c); /* then send it */
return;
}
/* get a character from the modem, but wait a maximum of (seconds)
tenths of a second. Return TIMEOUT on end. */
int modin(tenths)
int tenths;
{
char c;
tenths *=SPS; /* make into loop count, */
while ( !((c=inp(MSTAT)) &RDA) && --tenths); /* counter */
if (tenths ==0)
return (TIMEOUT);
else return(inp(MDATA)); /* get the char, */
}
/* appends [Y/N] to question asked, returns TRUE if yes. */
int ask(question)
char *question;
{
char c;
printf ("%s [ Y/N ]",question);
clreol();
while (1) {
c = lconin();
if(tolower(c) == 'y')
return(TRUE);
if(tolower(c) == 'n')
return (FALSE);
}
}
/* Wait until any character but control-S is detected, and return it.
Watch the keyboard for a break too. */
char wait()
{
int w;
w =CNTRLS;
while (w ==CNTRLS) {
if (modstat())
w =modin(1);
if (keyhit() ==STOPCHAR)
w =STOPCHAR;
}
return (w);
}
/* Cursor positioning routines. */
/* Clear the screen, leave the cursor at the BOTTOM,left. */
int clear()
{
int i;
place(BOTTOM,0); /* put the cursor at bottom. */
for(i=0; i<26; i++)
lconout('\n');
return;
}
/* Function PLACE at 'line' and 'column'. Lines are numbered 0-23 from
top to bottom, columns 0-79 from left to right. make sure this
routine is correct for your terminal. */
place(line,column) /* line, column */
char line,column; /* coordinates */
{
printf("%s%c%c",LEADIN,line+32,column+32);
return;
}
/* If a key is hit, return the key, else 0 */
char keyhit()
{
return(bdos(6,0xff));
}
clreol()
{
printf("%s",CLREOL);
return;
}
/* Get an input string; return NULL if error or empty line. Provide the
usual minimum editing capabilities. */
getstring(string)
char string[];
{
int count;
char c;
count= 0;
while (1) {
c= lconin(); /* get a character, */
switch (c) {
case CR: /* process it, */
case LF:
string[count]= 0x00; /* terminate string, */
return(count); /* return string length */
break;
case 0x08:
case 0x7f: /* delete character */
case 0x13:
if (count) {
--count; /* one less char, */
printf("\010 \010");
}
break;
case 0x18:
case 0x15: /* delete line */
case 0x19:
case 0x03:
while (count) {
--count;
printf("\010 \010");
}
break;
case 0x12: /* retype line, */
while (string[count]) {
lconout(string[count++]);
}
break;
default: /* insert character */
if ((c > 0x1f) && (count < 80) ) {
string[count++] =toupper(c);
string[count]= 0x00;
lconout(c);
} else
lconout(0x07);
break;
}
}
}
/* end */